import type { ComponentStyleIdType } from '../../types/interfaces'
import { UIComponent } from '../../lib/abstract'
import { ToolKit } from '../../lib/utils/toolkit'
import { ComponentStyleId, logger, PROJECT_PREFIX } from '../../types/constants'
import { FILE_UPLOAD_MODE, SYMBOLS } from '../../types/interfaces'
import { ScriptNotification } from '../notification'
import { TextInput } from '../text_input'

export interface uploadInputProps {
  id: string
  label: string
  urlValue: string
  mode: FileUploadMode
  onUrlChange: (value: string, componentId: FileUploadMode) => void
  onFileChange: (files: FileList | null, componentId: FileUploadMode) => void
  placeholder?: string
  accept?: string
}

/**
 * 上传输入框配置
 */
interface UploadInputConfig {
  maxFileSize?: number // 单个图片文件最大体积，单位MB
  allowedMimeTypes?: string[] | null // 允许的文件类型
}

const btnTextType = {
  select: '选择',
  change: '更换',
}

/**
 * 默认上传输入框配置
 */
const DEFAULT_CONFIG: UploadInputConfig = {
  maxFileSize: 20,
  allowedMimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
}

/**
 * 上传输入框样式类
 */
const UPLOAD_INPUT_CLASSES = {
  container: 'combined-upload-input',
  imagePreview: 'image-preview',
  urlContainer: 'url-input-container',
  fileUploadBtn: 'file-upload-btn',
  clearBtn: 'clear-btn',
  fileInput: 'file-input',
  urlInput: 'url-input',
  imagePreviewWrapper: 'image-preview-wrapper',
}

/**
 * 文件操作状态常量
 */
export const FileOperationStatus = {
  // 文件读取相关状态
  FILE_READ_SUCCESS: 'FILE_READ_SUCCESS',
  FILE_READ_FAILED: 'FILE_READ_FAILED',
  FILE_READ_EMPTY: 'FILE_READ_EMPTY',

  // 文件验证相关状态
  FILE_VALIDATION_SUCCESS: 'FILE_VALIDATION_SUCCESS',
  FILE_TOO_LARGE: 'FILE_TOO_LARGE',
  INVALID_FILE_TYPE: 'INVALID_FILE_TYPE',

  // 文件选择相关状态
  NO_FILE_SELECTED: 'NO_FILE_SELECTED',
  FILE_SELECTED: 'FILE_SELECTED',

  // 上传相关状态
  UPLOAD_SUCCESS: 'UPLOAD_SUCCESS',
  UPLOAD_FAILED: 'UPLOAD_FAILED',

  // 其他状态
  RESET_SUCCESS: 'RESET_SUCCESS',
} as const

export type FileOperationStatusType = typeof FileOperationStatus[keyof typeof FileOperationStatus]

const FileStatusMessages: Record<FileOperationStatusType, string> = {
  [FileOperationStatus.FILE_READ_SUCCESS]: '文件读取成功',
  [FileOperationStatus.FILE_READ_FAILED]: '文件读取失败',
  [FileOperationStatus.FILE_READ_EMPTY]: '文件读取结果为空',
  [FileOperationStatus.FILE_VALIDATION_SUCCESS]: '文件验证通过',
  [FileOperationStatus.FILE_TOO_LARGE]: '文件大小超过限制',
  [FileOperationStatus.INVALID_FILE_TYPE]: '不支持的文件类型',
  [FileOperationStatus.NO_FILE_SELECTED]: '未选择文件',
  [FileOperationStatus.FILE_SELECTED]: '已选择文件',
  [FileOperationStatus.UPLOAD_SUCCESS]: '文件上传成功',
  [FileOperationStatus.UPLOAD_FAILED]: '文件上传失败',
  [FileOperationStatus.RESET_SUCCESS]: '文件重置成功',
}

export class UploadInput extends UIComponent<uploadInputProps> {
  id: string = 'upload-input'
  readonly description: string = '带输入框的图片上传'
  readonly styleId: string = ComponentStyleId[this.name]
  private readonly uploadConfig: UploadInputConfig
  private readonly hasImageTips: string = '已上传图片'
  element: JQuery<HTMLElement> | undefined = undefined
  private componentId: Record<FileUploadMode, string>

  get name(): ComponentStyleIdType {
    return 'UploadInput'
  }

  get className(): string {
    return UPLOAD_INPUT_CLASSES.container
  }

  constructor(props: uploadInputProps, config?: UploadInputConfig) {
    super(props)
    this.uploadConfig = { ...DEFAULT_CONFIG, ...config }

    this.componentId = {
      url: `${this.props.id}-url`,
      file: `${this.props.id}-file`,
    }

    // 注册样式
    this.registerStyles()
  }

  ELEMENT_SELECTORS = {
    urlInput: { selector: `#${this.props.id}-url`, dynamic: false, unique: true },
    fileInput: { selector: `#${this.props.id}-file`, dynamic: false, unique: true },
    imagePreview: { selector: `.${UPLOAD_INPUT_CLASSES.imagePreview}`, dynamic: false, unique: false },
    imagePreviewImg: { selector: `.${UPLOAD_INPUT_CLASSES.imagePreview} img`, dynamic: false, unique: false },
    btnContent: { selector: `.${UPLOAD_INPUT_CLASSES.fileUploadBtn} span`, dynamic: false, unique: false },
    clearBtn: { selector: `.${UPLOAD_INPUT_CLASSES.clearBtn}`, dynamic: false, unique: false },
  }

  render(): string {
    const { label, mode } = this.props as uploadInputProps
    const hasImage = (this.props.urlValue?.length || 0) > 0
    const btnText = hasImage ? btnTextType.change : btnTextType.select

    return `
      <label>${label}</label>
      <div class="${UPLOAD_INPUT_CLASSES.container}">
        <div class="${UPLOAD_INPUT_CLASSES.urlContainer}">
          ${this.renderUrlInput(hasImage, mode)}
        </div>
        <label for="${this.componentId.file}" class="${UPLOAD_INPUT_CLASSES.fileUploadBtn}">
          <span>${btnText}</span>
          ${this.renderFileInput(hasImage)}
        </label>
        ${this.renderClearButton(hasImage)}
        ${this.renderImagePreview(hasImage)}
      </div>
    `
  }

  /**
   * 渲染 URL 输入框
   * @param hasImage 是否有图片
   * @param mode 上传模式
   * @returns {string} URL 输入框 HTML
   */
  private renderUrlInput(hasImage: boolean, mode: FileUploadMode): string {
    return new TextInput({
      id: this.componentId.url,
      type: FILE_UPLOAD_MODE.URL,
      placeholder: this.props.placeholder || '输入文件URL',
      value: hasImage && mode === FILE_UPLOAD_MODE.FILE ? this.hasImageTips : this.props.urlValue,
      inputClassName: UPLOAD_INPUT_CLASSES.urlInput,
    }).render()
  }

  /**
   * 渲染文件输入框
   * @returns {string} 文件输入框 HTML
   */
  private renderFileInput(hasImage: boolean): string {
    return new TextInput({
      id: this.componentId.file,
      type: FILE_UPLOAD_MODE.FILE,
      placeholder: `${hasImage ? '更换' : '选择'}`,
      accept: this.props.accept || 'image/*',
      value: '',
      inputClassName: UPLOAD_INPUT_CLASSES.fileInput,
    }).render()
  }

  /**
   * 渲染图片预览
   * @returns {string} 图片预览 HTML
   */
  private renderImagePreview(hasImage: boolean): string {
    const style = hasImage ? '' : 'display: none !important;'
    const url = hasImage ? this.props.urlValue : ''

    return `
      <div class="${UPLOAD_INPUT_CLASSES.imagePreview}" style="${style}">
        <img src="${url}" alt=""/>
      </div>
    `
  }

  /**
   * 渲染清除按钮
   * @returns {string} 清除按钮 HTML
   */
  private renderClearButton(hasImage: boolean): string {
    const style = hasImage && this.props.urlValue ? '' : 'display: none !important;'
    return `
      <button class="${UPLOAD_INPUT_CLASSES.clearBtn}" style="${style}">清除</button>
    `
  }

  private handleUrlChange() {
    const newUrl = this.getElement('urlInput')?.val() as string

    if (newUrl === this.hasImageTips)
      return

    this.updateImageByUrl(newUrl, false)

    this.props.onUrlChange(newUrl, this.componentId.url)
  }

  async handleFileChange(): Promise<string | null> {
    const $fileInput = this.getElement('fileInput')
    const files = $fileInput?.prop('files')

    const $btnContent = this.getElement('btnContent')
    const $urlInput = this.getElement('urlInput')

    if (!files || files.length === 0) {
      logger.warn(`[${this.name}] ${FileStatusMessages[FileOperationStatus.NO_FILE_SELECTED]}`)
      return null
    }

    const file = files[0]
    logger.important(`[${this.name}] 检测到上传文件: ${file.name}`)

    // 文件验证
    if (!this.validateFile(file)) {
      $fileInput?.val('')
      return null
    }

    try {
      const dataUrl = await ToolKit.browser.file.readFileAsDataURL(file)

      // 确保dataUrl有效
      if (!dataUrl) {
        logger.warn(`[${this.name}] ${FileStatusMessages[FileOperationStatus.FILE_READ_EMPTY]}`)
        return null
      }

      this.props.onFileChange(files, this.componentId.file)

      // 更新UI
      this.handleUpdateImage(dataUrl)
      $btnContent?.text(btnTextType.change)
      $urlInput?.val(this.hasImageTips)
      logger.info(`[${this.name}] ${FileStatusMessages[FileOperationStatus.UPLOAD_SUCCESS]}`)
      return dataUrl // 返回 dataUrl 供外部使用
    }
    catch (error: any) {
      logger.error(`[${this.name}] ${FileStatusMessages[FileOperationStatus.FILE_READ_FAILED]}`, error)
      return null
    }
    finally {
      // 重置文件输入框
      $fileInput?.val('')
    }
  }

  /**
   * 处理清除操作
   */
  private handleClear() {
    // 回调清空的URL
    this.props.onFileChange(null, this.componentId.file)
    this.resetUI()
    logger.info(`[${this.name}] ${FileStatusMessages[FileOperationStatus.RESET_SUCCESS]}`)
  }

  /**
   * 重置 UI
   */
  private resetUI() {
    this.resetImagePreviewUI()
    this.resetUrlInputUI()
  }

  /**
   * 重置图片预览 UI
   */
  private resetImagePreviewUI() {
    // 隐藏预览图片并清除背景
    this.handleResetImage()
    // 隐藏清除按钮
    const $clearBtn = this.getElement('clearBtn')
    ToolKit.dom.setElementCssAttribute($clearBtn)
  }

  /**
   * 重置 URL 输入框 UI
   */
  private resetUrlInputUI() {
    // 重置按钮文本
    this.getElement('btnContent')?.text(btnTextType.select)
    // 重置文本输入框
    this.getElement('urlInput')?.val('')
    this.props.onUrlChange('', this.componentId.url)
  }

  /**
   * 文件验证
   * @param file 文件
   * @returns {boolean} 是否通过验证
   */
  private validateFile(file: File): boolean {
    const notification = new ScriptNotification()
    // 文件大小校验
    if (file.size > (this.uploadConfig.maxFileSize || 20) * 1024 * 1024) {
      notification.show({
        title: '图片上传状态',
        text: `文件大小不能超过 ${this.uploadConfig.maxFileSize}MB`,
        timeout: 5000,
        highlight: true,
        onClick: () => {},
      })
      logger.warn(`[${this.name}] ${FileStatusMessages[FileOperationStatus.FILE_TOO_LARGE]}`)
      return false
    }

    // 文件类型校验
    if (this.uploadConfig.allowedMimeTypes
      && !this.uploadConfig.allowedMimeTypes.includes(file.type)) {
      notification.show({
        title: '图片上传状态',
        text: `只支持图片文件: ${this.uploadConfig.allowedMimeTypes.join(', ')}`,
        timeout: 5000,
        highlight: true,
        onClick: () => {},
      })
      logger.warn(`[${this.name}] ${FileStatusMessages[FileOperationStatus.INVALID_FILE_TYPE]}`)
      return false
    }

    return true
  }

  /**
   * 绑定事件
   */
  bindEvents(): void {
    // 更新图片预览
    if (this.props.mode === FILE_UPLOAD_MODE.URL) {
      this.updateImageByUrl(this.props.urlValue, true)
    }

    // URL失焦、回车事件
    this.getElement('urlInput')
      ?.on('blur', () => this.handleUrlChange())
      .on('keydown', (e) => {
        if (e.key === 'Enter') {
          this.handleUrlChange()
          this.getElement('urlInput')?.trigger('blur')
        }
      })

    // 文件选择变化
    this.getElement('fileInput')?.on('change', () => this.handleFileChange())

    // 清除按钮点击
    this.getElement('clearBtn')?.on('click', () => this.handleClear())
  }

  getStyles(): string {
    return styles()
  }

  reset(): void {
    super.reset()

    // 重置 URL 输入框
    this.getElement('urlInput')?.val('').trigger('blur')
    // 重置文件输入框
    this.getElement('fileInput')?.val('')
    // 重置预览图片
    this.handleResetImage()
  }

  /**
   * 检查图片 URL 是否有效
   * @param url 图片URL
   * @param forceUpdate 是否强制更新历史图片预览
   */
  updateImageByUrl(url: string | undefined, forceUpdate: boolean = false): void {
    url = url || ''

    // 1. 对当前输入框的 URL 创建临时图片元素
    const url_img = new Image()
    url_img.src = url
    url_img.onload = url_img.onerror = () => {
      // 更新图片预览
      this.getElement('btnContent')?.text(btnTextType.select)
      this.handleUpdateImage(url)
    }

    // 2. 对历史图片进行检查并处理
    const historySrc = this.getElement('imagePreviewImg')?.attr('src')

    if ((historySrc && url.length === 0) || forceUpdate) {
      const file_img = new Image()
      file_img.src = historySrc || ''

      // 如果输入的 URL 无效且历史图片预览也无效时，则重置图片预览
      file_img.onerror = () => {
        // 强制更新用于初始化的场景，所以需要重置所有
        forceUpdate ? this.resetUI() : this.handleResetImage()
        logger.notice(`[${this.name}] ${SYMBOLS.STATUS.INFO} 检查到图片 URL 输入框被清空且历史图片无效，重置预览`)
      }
    }
  }

  /**
   * 更新图片预览
   * @param url 图片 URL
   */
  handleUpdateImage(url: string): void {
    const $imagePreview = this.getElement('imagePreview')
    if ($imagePreview && url && url.length > 0) {
      $imagePreview.show()
      this.getElement('imagePreviewImg')?.attr('src', url)
      this.getElement('clearBtn')?.show()
    }
  }

  /**
   * 重置图片预览
   */
  handleResetImage(): void {
    const $imagePreview = this.getElement('imagePreview')
    ToolKit.dom.setElementCssAttribute($imagePreview)

    this.getElement('imagePreviewImg')?.attr('src', '')

    const $clearBtn = this.getElement('clearBtn')
    ToolKit.dom.setElementCssAttribute($clearBtn)
  }
}

export function styles(): string {
  return `
    .${UPLOAD_INPUT_CLASSES.container} {
      all: initial !important;
      display: flex !important;
      gap: 10px !important;
      width: 100% !important;
      align-items: center !important;
      
      .${UPLOAD_INPUT_CLASSES.urlContainer} {
        flex: 1 !important;
        min-width: 0 !important;
        
        input {
          width: 100% !important;
          padding: 8px 12px !important;
          border: 1px solid var(--${PROJECT_PREFIX}-theme-border-neutral) !important;
          border-radius: 4px !important;
          font-size: 14px !important;
          
          &:focus {
            outline: none !important;
          }
        }
      }
      
      .${UPLOAD_INPUT_CLASSES.fileUploadBtn}, 
      .${UPLOAD_INPUT_CLASSES.clearBtn} {
        all: initial !important; /* 重置所有样式 */
        display: inline-flex !important;
        align-items: center !important;
        justify-content: center !important;
        padding: 8px 16px !important;
        background-color: #f8f9fa !important;
        border: 1px solid #ced4da !important;
        border-radius: 4px !important;
        font-size: 14px !important;
        cursor: pointer !important;
        transition: all 0.2s ease !important;
        white-space: nowrap !important;
        user-select: none !important;
        
        &:hover {
          background-color: #e9ecef !important;
        }
        
      }
      .${UPLOAD_INPUT_CLASSES.fileInput} {
        display: none !important;
      }
    }
    
    .${UPLOAD_INPUT_CLASSES.imagePreview} {
      width: 32px !important;
      height: 32px !important;
      background-size: cover !important;
      background-position: center !important;
      border: none !important;
      left: 0 !important;
      top: calc(100% + 5px) !important;
      z-index: 10 !important;
      img {
        width: 100% !important;
        height: 100% !important;
        object-fit: cover !important;
      }
    }
  `
}
